--HASKELL HandIn, deadline november 30.
--Student: Mathias Andersen, dat5, group d502e11. 

type Name = String
type Price = Int   -- prices are specified in cents here
type BarCode = Int
type Database = [(BarCode,Name,Price)]

db = [(4719, "Fish Fingers", 121),
      (5643, "Nappies", 1010),
      (1111, "Hula Hoops", 21),
      (1112, "Hula Hoops (Giant)", 133),
      (3814, "Orange Jelly", 58),
      (1234, "Dry Sherry, 1lt", 540)] :: Database
                                         
codes = [1234,4719,1112,3814,1234] :: [BarCode]

--the lookupBarCode uses a guard to scan through the list of barcodes. If the barcode of the head element of the list is matched then the name and price of that element is returned, else the function tries to make a match in the rest of the list. I have included a special value ("empty",0) to be returned if no match is found. If this is not included the program will crash if no match is found (non-exhaustive pattern exception).
lookupBarCode :: BarCode -> Database -> (Name,Price)
lookupBarCode bar_code [] = ("empty",0)
lookupBarCode bar_code (h:t) | code == bar_code = (name,price)
                             | otherwise = lookupBarCode bar_code t
  where (code,name,price) = h

--this function generates a number of dots for a bill-item. It recursively appends a dot and decreases the counter until 0 is reached. 
dotGen n | n == 0 = []
         | otherwise = '.':dotGen(n-1)

--the formatline function is able to format a line for the bill. It needs to calculate the correctr number of dots that is needed. To do this one has to format the price according to its size. The show method is similar to the java toString() method. 
formatLine :: (Name,Price) -> String
formatLine(name,price) = name ++ dotGen(numberOfDots) ++ ['$'] ++ makePrice 
  where numberOfDots = 30 - length name - length makePrice -1 
        makePrice 
          | price < 10 = "0.0" ++ show price
          | price < 100 = "0." ++ show price
          | otherwise = show price

--the makeTotalSum function takes a list of barcodes (a shopping list) and returns the total sum of the items. This is done by recursively adding the price of each item to the accumulated sum. It uses the lookupBarCode function to get the name and price of each item. When the list is empty, 0 is added to the accumulated sum, and the function returns the sum.  
makeTotalSum :: [BarCode] -> Database -> Price
makeTotalSum [] _ = 0
makeTotalSum (h:t) dataBase = price + makeTotalSum t dataBase
  where (name,price) = lookupBarCode h dataBase

--the formatTotal function does exactly the same as the formatLine function, only here there is no need for the Name argument, since this is always just "Total". 
formatTotal(totalSum) = "Total" ++ dotGen(numberOfDots) ++ ['$'] ++ totalSumString 
  where numberOfDots = 30 - 5 - length totalSumString -1 
        totalSumString 
          | totalSum < 10 = "0.0" ++ show totalSum
          | totalSum < 100 = "0." ++ show totalSum
          | otherwise = show totalSum

--the makeBill function uses the foldr function from the standard prelude. Foldr is equivalent to the fold function from the lectures. It uses the operator (function) f on the elements in the list it receives (listOfCodes), and in the end it uses the given unit element (i.e. wrt. the given operator, here it is []) to stop the recursion. At each step in the recursion a item line in the bill is formatted and a newline character is added, together with the rest of the recursively formatted lines (b). When the foldr method returns the formatTotal is called and appended. This adds the total to the end of the bill. 
makeBill :: [BarCode] -> Database -> String
makeBill listOfCodes dataBase = foldr f [] listOfCodes ++ formatTotal(makeTotalSum listOfCodes dataBase) 
  where
    f a b = formatLine(lookupBarCode a dataBase) ++ "\n" ++ b

--to display the results the makeBill function is wrapped in a call to putstring. This ensures that the newline characters are read properly, and also we can display the results by simply typing "main".
main = putStr (makeBill codes db)
